package org.xx.armory.rpc.decorators;

import org.xx.armory.rpc.Decorator;
import org.xx.armory.security.AuthenticationToken;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.stream.Collectors;

import static org.xx.armory.commons.Validators.notBlank;
import static org.xx.armory.commons.Validators.notNull;

public class InMemoryAuthenticationDecorator
        extends AbstractAuthenticationDecorator
        implements Decorator {
    private final Map<String, Map<String, String>> tokens;

    public InMemoryAuthenticationDecorator() {
        tokens = new ConcurrentSkipListMap<>();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected AuthenticationToken loadToken(
            String tokenId
    ) {
        tokenId = notBlank(tokenId, "tokenId").trim();

        final Map<String, String> s = this.tokens.computeIfAbsent(tokenId, (newTokenId) -> new LinkedHashMap<>());
        AuthenticationToken token = mapToToken(s);
        if (token == null) {
            token = AuthenticationToken.guest();
        }

        return token;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void saveToken(
            String tokenId,
            AuthenticationToken token
    ) {
        tokenId = notBlank(tokenId, "tokenId").trim();

        if (token != null) {
            this.tokens.put(tokenId, tokenToMap(token));
        } else {
            this.tokens.remove(tokenId);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void signIn(AuthenticationToken token) {
        notNull(token, "token");

        final Collection<String> removedTokenIdList = this.tokens.keySet()
                                                                 .stream()
                                                                 .filter(tokenId -> {
                                                                     final Map<String, String> map = this.tokens.get(tokenId);
                                                                     return map != null && Objects.equals(token.getUserId(), map.get(UID_KEY_NAME));
                                                                 })
                                                                 .collect(Collectors.toList());

        removedTokenIdList.forEach(this.tokens::remove);
    }
}
